package io.github.stary1993.kite.admin.service.impl;


import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import com.github.yulichang.base.MPJBaseServiceImpl;
import io.github.stary1993.kite.admin.entity.SysRole;
import io.github.stary1993.kite.admin.entity.SysUser;
import io.github.stary1993.kite.admin.entity.SysUserRole;
import io.github.stary1993.kite.admin.mapper.SysUserMapper;
import io.github.stary1993.kite.admin.mapper.SysUserRoleMapper;
import io.github.stary1993.kite.admin.service.SysRoleService;
import io.github.stary1993.kite.admin.service.SysUserService;
import io.github.stary1993.kite.admin.vo.UserExcelVO;
import io.github.stary1993.kite.admin.vo.UserVO;
import io.github.stary1993.kite.core.constant.CommonConstants;
import io.github.stary1993.kite.core.exception.ErrorCodes;
import io.github.stary1993.kite.core.util.MessageUtils;
import io.github.stary1993.kite.core.util.R;
import io.github.stary1993.kite.excel.vo.ErrorMessage;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.validation.BindingResult;

import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * @author stary1993
 */
@Slf4j
@Service
@AllArgsConstructor
public class SysUserServiceImpl extends MPJBaseServiceImpl<SysUserMapper, SysUser> implements SysUserService {


    private static final PasswordEncoder ENCODER = new BCryptPasswordEncoder();


    private final SysRoleService sysRoleService;


    private final SysUserRoleMapper sysUserRoleMapper;


    /**
     * @param userId
     * @return
     */
    @Override
    public UserVO detailsById(Long userId) {
        SysUser sysUser = this.getById(userId);
        UserVO userVO = BeanUtil.copyProperties(sysUser, UserVO.class);
        List<SysRole> roleList = sysRoleService.findRolesByUserId(userId);
        userVO.setRoleList(roleList);
        return userVO;
    }

    /**
     * @param username
     * @return
     */
    @Override
    public UserVO detailsByUsername(String username) {
        SysUser sysUser = this.lambdaQuery().eq(SysUser::getUsername, username).one();
        UserVO userVO = BeanUtil.copyProperties(sysUser, UserVO.class);
        List<SysRole> roleList = sysRoleService.findRolesByUserId(sysUser.getUserId());
        userVO.setRoleList(roleList);
        return userVO;
    }

    @Override
    public UserVO detailsByPhone(String phone) {
        SysUser sysUser = this.lambdaQuery().eq(SysUser::getPhone, phone).one();
        UserVO userVO = BeanUtil.copyProperties(sysUser, UserVO.class);
        List<SysRole> roleList = sysRoleService.findRolesByUserId(sysUser.getUserId());
        userVO.setRoleList(roleList);
        return userVO;
    }


    @Override
    public List<UserExcelVO> exportUser() {
        // 根据数据权限查询全部的用户信息
        List<SysUser> userList = this.list();
        // 转换成execl 对象输出
        return userList.stream().map(user -> {
            UserExcelVO excelVO =  BeanUtil.copyProperties(user, UserExcelVO.class);
            List<SysRole> roleList = sysRoleService.findRolesByUserId(user.getUserId());
            excelVO.setRoleNameList(roleList.stream().map(SysRole::getRoleName).collect(Collectors.joining(StrUtil.COMMA)));
            return excelVO;
        }).collect(Collectors.toList());
    }

    /**
     * excel 导入用户, 插入正确的 错误的提示行号
     *
     * @param excelVOList   excel 列表数据
     * @param bindingResult 错误数据
     * @return ok fail
     */
    @Override
    public R importUser(List<UserExcelVO> excelVOList, BindingResult bindingResult) {
        // 通用校验获取失败的数据
        List<ErrorMessage> errorMessageList = (List<ErrorMessage>) bindingResult.getTarget();
        List<SysRole> roleList = sysRoleService.list();

        // 执行数据插入操作 组装 UserDto
        for (UserExcelVO excel : excelVOList) {
            // 个性化校验逻辑
            List<SysUser> userList = this.list();

            Set<String> errorMsg = new HashSet<>();
            // 校验用户名是否存在
            boolean existUserName = userList.stream()
                    .anyMatch(sysUser -> excel.getUsername().equals(sysUser.getUsername()));

            if (existUserName) {
                errorMsg.add(MessageUtils.getMessage(ErrorCodes.SYS_USER_USERNAME_EXISTING, excel.getUsername()));
            }

            // 判断输入的角色名称列表是否合法
            List<String> roleNameList = StrUtil.split(excel.getRoleNameList(), StrUtil.COMMA);
            List<SysRole> roleCollList = roleList.stream()
                    .filter(role -> roleNameList.stream().anyMatch(name -> role.getRoleName().equals(name)))
                    .collect(Collectors.toList());

            if (roleCollList.size() != roleNameList.size()) {
                errorMsg.add(MessageUtils.getMessage(ErrorCodes.SYS_ROLE_ROLENAME_INEXISTENCE, excel.getRoleNameList()));
            }

            // 数据合法情况
            if (CollUtil.isEmpty(errorMsg)) {
                insertExcelUser(excel, roleCollList);
            } else {
                // 数据不合法情况
                errorMessageList.add(new ErrorMessage(excel.getLineNum(), errorMsg));
            }

        }

        if (CollUtil.isNotEmpty(errorMessageList)) {
            return R.failed(errorMessageList);
        }
        return R.ok();
    }

    /**
     * 插入excel User
     */
    private void insertExcelUser(UserExcelVO excel,  List<SysRole> roleCollList) {
        SysUser sysUser = new SysUser();
        sysUser.setUsername(excel.getUsername());
        sysUser.setPhone(excel.getPhone());
        sysUser.setNickname(excel.getNickname());
        sysUser.setName(excel.getName());
        sysUser.setEmail(excel.getEmail());
        // 批量导入初始密码为手机号
        sysUser.setPassword(sysUser.getPhone());
        sysUser.setDelFlag(CommonConstants.STATUS_NORMAL);
        sysUser.setCreateBy(sysUser.getUsername());
        sysUser.setPassword(ENCODER.encode(sysUser.getPassword()));
        this.save(sysUser);
        List<SysUserRole> userRoles = roleCollList.stream().map(role -> {
            SysUserRole userRole = new SysUserRole();
            userRole.setUserId(sysUser.getUserId());
            userRole.setRoleId(role.getRoleId());
            return userRole;
        }).collect(Collectors.toList());

        sysUserRoleMapper.insert(userRoles);

    }
}
